#include "ConnectWidget.h"
#include "RoQuery.h"

#include <QMessageBox>

ConnectDialog::ConnectDialog(QTabWidget* parent) :
        super(parent),
        parentTabWidget(parent) // Т.к Qt меняет parent на QStackedWidget
{
    this->setupUi(this);
    setInitialData();

    setActions();
}

void ConnectDialog::addInfoAttribute()
{
    AttributeWidget* attributeWidget = new AttributeWidget(infoWidget);

    QVBoxLayout* internalLayOut = qobject_cast<QVBoxLayout*>(infoWidget->layout());
    internalLayOut->insertWidget(
                                internalLayOut->count()-2, //Чтобы кнопка "+" была ниже добавленного виджета.
                                attributeWidget);

    connect(attributeWidget->deleteButton, SIGNAL(clicked()),
            this, SLOT(deleteInfoAttribute()));

    infoWidget->setMinimumHeight(infoWidget->minimumHeight() + attributeWidget->minimumHeight());

    parentTabWidget->resize( parentTabWidget->width(),
                             parentTabWidget->height() + attributeWidget->height());

    //parentTabWidget->adjustSize();
    // Сохраним его.
    attributeWidgets.push_back(attributeWidget);
}

void ConnectDialog::addInfoChatWidget()
{
    QString infoChatName = infoChatlineEdit->text();
    if( infoChatReceivers[infoChatName].isNull() )
    {
        QPointer<InfoChatWidget> infoChatWidget = new InfoChatWidget(m_component, parentTabWidget, infoChatName);
        infoChatReceivers[infoChatName] = infoChatWidget;

        parentTabWidget->addTab(infoChatWidget, QString("<info %1>").arg(infoChatName));

//        //Соединяем сигнал о получении info-шки в компоненте со слотом логирования в infoChatWidget.
//        connect (m_component.data(), SIGNAL(receivedNewInfo(RoInfoStanza)),
//                 infoChatWidget.data(), SLOT(logInfoText(RoInfoStanza)));
    }
    parentTabWidget->setCurrentWidget(infoChatReceivers[infoChatName]);
}

void ConnectDialog::callFunction()
{
    ConnectionTextEdit->append( QString("Послан запрос к %1::%2.")
                                .arg(recepientLineEdit->text(), m_chosenMethod) );

    QPointer<ConversationWidget> conversationWidget = new ConversationWidget(parentTabWidget);
    parentTabWidget->addTab(conversationWidget, m_chosenMethod);
    parentTabWidget->setCurrentWidget(conversationWidget);

    // Формируем и отправляем запрос к компоненту.
    QPointer<RoQuery> query;
    query = m_component->createSimpleQuery(recepientLineEdit->text(), m_chosenMethod);

    query->setResultAcceptor( conversationWidget, SLOT(receive_query_result(bool,QDomDocument)) );
    query->setDataAcceptor( conversationWidget, SLOT(receive_query_datum(QDomDocument)) );
    if( !argumentsLineEdit->text().isEmpty() )
    {
        QDomDocument argument;
        argument.setContent(argumentsLineEdit->text());
        query->exec(argument);
    }
    else
        query->exec();
}

void ConnectDialog::connectToServer()
{
    connectButton->hide(); // connectButton->setVisible(false);
    disConnectButton->show();

    AddresslineEdit->setEnabled(false);
    ConnectionTextEdit->append("Соединяемся с сервером...");

    m_module->connectToNetwork(AddresslineEdit->text());
}


void ConnectDialog::deleteInfoAttribute()
{
    AttributeWidget* attributeWidget = qobject_cast<AttributeWidget*>(sender()->parent());

    infoWidget->setMinimumHeight(infoWidget->minimumHeight() - attributeWidget->minimumHeight());
    parentTabWidget->resize( parentTabWidget->width(),
                             parentTabWidget->height() - attributeWidget->height());

    attributeWidgets.removeAt( attributeWidgets.indexOf(attributeWidget) );
    attributeWidget->close();
    //parentTabWidget->adjustSize();
}


void ConnectDialog::disconnectFromServer()
{
    if (parentTabWidget->count() > 1)
    {
        if ( !closeOtherTabs())
            return;
    }

    m_module->disconnectFromNetwork();
    modelItems.clear();

    ComponentsTreeView->model()->removeRows( 0, ComponentsTreeView->model()->rowCount() );
    ComponentsTreeView->setFocus(); //иначе, содержимое исчезнет только при попадании курсора на объект.

    disConnectButton->hide();
    connectButton->show();

    AddresslineEdit->setEnabled(true);
    TestButton->setEnabled(false);

    recepientLineEdit->clear();
    senderLineEdit->clear();
    argumentsLineEdit->clear();

    infoFromLineEdit->clear();
    infoNameLineEdit->clear();
    sendInfoButton->setEnabled(false);

    ConnectionTextEdit->append(QString("Отключились от сервера"));
}

void ConnectDialog::gotAllMethods()
{
    // Записываем сформированную модель.
    m_model = new ComponentsTreeModel(modelItems);
    ComponentsTreeView->setModel(m_model);

    // прячем 3 столбец с примерами.
    ComponentsTreeView->setColumnHidden(2, true);
}

void ConnectDialog::gotComponents(const QMap<QString, QString> gotComponents)
{
    ConnectionTextEdit->append("Список компонентов получен.");

    amountOfComponents = gotComponents.count();

    for (QMap<QString, QString>::const_iterator gotComponent = gotComponents.begin();
         gotComponent != gotComponents.end();
         gotComponent++)
    {
        Component* component = new Component(gotComponent.key(), gotComponent.value());
        modelItems << *component;

        QPointer<RoQuery> query = new RoQuery();
        query = m_component->createSimpleQuery(component->name, "query-interface");
        query->exec();
    }
}

void ConnectDialog::gotMethods(const QString& gotComponentName, const QList<Component::Function>& gotMethods)
{
    foreach (Component::Function function, gotMethods)
    {
        for (QList<Component>::iterator component = modelItems.begin();
        component != modelItems.end(); component++)
        {
            if ( (*component).name == gotComponentName )
            {
                (*component).functions << function;
                break;
            }
        }
    }
    if( --amountOfComponents == 0)
    {
        emit allMethodsReceived();
    }
}

void ConnectDialog::gotNewInfo(const RoInfoStanza &stanza)
{
    QString stanzaName = stanza.name();
//    if( infoReceivers[stanzaName].isNull() ) // Проверяем, есть ли виджет принимающий такую info-шечку.
//    {
//        // воспользуемся ConversationWidget для создания окошечка с info-шечкой :)
//        QPointer<ConversationWidget> conversationWidget =
//                new ConversationWidget(parentTabWidget,
//                                       Conversation::Info, //тип = Info
//                                       stanzaName); // собсвтеннно имя info-шечки.
//                                                    // по нему и будем фильтровать.

//        // запомним виджет и имя info-шечки.
//        infoReceivers[stanzaName] = conversationWidget;

//        parentTabWidget->addTab(conversationWidget, QString("<info %1>").arg(stanzaName));
//        parentTabWidget->setCurrentWidget(conversationWidget);
//        // Соединяем сигнал о получении info-шки в компоненте со слотом логирования в conversationWidget.
//        connect (m_component.data(), SIGNAL(receivedNewInfo(RoInfoStanza)),
//                 conversationWidget.data(), SLOT(logInfoText(RoInfoStanza)));
//        // Т.к. информация в этой info-шечке принадлежит созданному ConversationWidget, то её туда и перенаправим.
//        conversationWidget->logInfoText(stanza);
//    }
    if ( infoChatReceivers[stanzaName].isNull() )
    {
        QPointer<InfoChatWidget> infoChatWidget = new InfoChatWidget(m_component, parentTabWidget, stanzaName);
        infoChatReceivers[stanzaName] = infoChatWidget;

        parentTabWidget->addTab(infoChatWidget, QString("<info %1>").arg(stanzaName));

        // Соединяем сигнал о получении info-шки в компоненте со слотом логирования в infoChatWidget.
//        connect (m_component.data(), SIGNAL(receivedNewInfo(RoInfoStanza)),
//                 infoChatWidget.data(), SLOT(logInfoText(RoInfoStanza)));
        // Т.к. информация в этой info-шечке принадлежит созданному infoChatWidget, то её туда и перенаправим.
        infoChatWidget->logInfoText(stanza);
    }
    else
    {
        infoChatReceivers[stanzaName]->logInfoText(stanza);
    }
}

void ConnectDialog::handleConnectedSignal()
{
    ConnectionTextEdit->append("Соединение установлено.");
    ConnectionTextEdit->append("Получаем список компонентов...");
    getComponents();

    queryWidgetWithButton->show();
    queryWidget->hide();

//    infoCommandLinkButton->show();
//    infoFromLineEdit->setText(m_component->name());
//    infoNameLineEdit->setText("test");

    infoChatInitWidget->show();
    infoChatlineEdit->setText("test");

    sendInfoButton->setEnabled(true);
    parentTabWidget->adjustSize(); //Так, для красоты
}

void ConnectDialog::handleTreeViewClick(const QModelIndex &clickedIndex)
{
    senderLineEdit->clear();
    recepientLineEdit->clear();
    argumentsLineEdit->clear();
    argumentsLineEdit->setEnabled(false);

    if(clickedIndex.parent() == QModelIndex()) // Т.е это корневые элементы - сыновья 0-ого, абстрактного уровня.
    {
        TestButton->setEnabled(false);
        return;
    }
    // Теперь обрабатываются только item-ы описывающие функцию
    TestButton->setEnabled(true);
    QVariant dataMethodName;
    // Код который помогает получить имя функции
    if(clickedIndex.column() > 0)
    {
        int functionNameColumn = 0;
        QModelIndex functionIndex = m_model->index( clickedIndex.row(),
                                                    functionNameColumn,
                                                    clickedIndex.parent() );
        dataMethodName = m_model->data(functionIndex, Qt::DisplayRole);

    }
    else
        dataMethodName = m_model->data(clickedIndex, Qt::DisplayRole);

    QVariant dataExample;
    int exampleColumn = 2;
    dataExample = m_model->data( m_model->index(
                                                clickedIndex.row(),
                                                exampleColumn,
                                                clickedIndex.parent()
                                                ),
                                Qt::DisplayRole );
    if ( !dataExample.toString().isEmpty() )
    {
        argumentsLineEdit->setEnabled(true);
        setTextWithCursorInBegin(argumentsLineEdit, dataExample.toString());
    }

    m_chosenMethod = dataMethodName.toString();
    m_chosenComponent = clickedIndex.parent().data().toString();

    setTextWithCursorInBegin(senderLineEdit, m_component->name());
    setTextWithCursorInBegin(recepientLineEdit, m_chosenComponent);

}

void ConnectDialog::handleTreeViewDoubleClick(const QModelIndex &doubleClickedIndex)
{
    if(doubleClickedIndex.parent() == QModelIndex()) // Т.е это корневые элементы - сыновья 0-ого, абстрактного уровня.
    {
        return;
    }
    handleTreeViewClick(doubleClickedIndex);
    callFunction();
}

void ConnectDialog::sendInfo()
{
    RoInfoStanza info = RoInfoStanza( infoNameLineEdit->text() );

    info.setAttribute("from", infoFromLineEdit->text());

    foreach(AttributeWidget* attrWidget, attributeWidgets)
    {
        if( !attrWidget->attrNameEdit->text().isEmpty() &&
            !attrWidget->attrValueEdit->text().isEmpty() )
        {
            info.setBodyAttribute(attrWidget->attrNameEdit->text(),
                              attrWidget->attrValueEdit->text());
        }
    }
    m_component->sendStanza(info);
}

void ConnectDialog::showInfoWidget()
{
    if ( infoWidget->isHidden() )
    {
        infoWidget->show();
        parentTabWidget->resize( parentTabWidget->width(),
                                 parentTabWidget->height() + infoWidget->height() );
    }
    else
    {
        infoWidget->hide();
        parentTabWidget->resize( parentTabWidget->width(),
                                 parentTabWidget->height() - infoWidget->height() );
        //parentTabWidget->adjustSize();
    }
}

void ConnectDialog::showQueryWidget()
{
    //parentTabWidget->adjustSize();
    if ( queryWidget->isHidden() )
    {
        queryWidget->show();
        parentTabWidget->resize( parentTabWidget->width(),
                                 parentTabWidget->height() + queryWidget->height() );

    }
    else
    {
        queryWidget->hide();
        parentTabWidget->resize( parentTabWidget->width(),
                                 parentTabWidget->height() - queryWidget->height() );
    }
}

void ConnectDialog::tabCloseButtonPressed(int tabIndex)
{
    if (tabIndex == parentTabWidget->indexOf(this)) // Попали в первый(главный) таб.
    {
        if ( parentTabWidget->count() > 1 ) // Не только эта вкладка
        {
            closeOtherTabs();
        }
        else // Выходим.
        {
            disconnectFromServer();
            parentTabWidget->close();
        }
    }
    else // Закрываем другую вкладку.
    {
        ConversationWidget* widget;
        if ( (widget = qobject_cast<ConversationWidget*>(parentTabWidget->widget(tabIndex))) )
                widget -> closeProperly();
        else
            qobject_cast<InfoChatWidget*>(parentTabWidget->widget(tabIndex)) -> closeProperly();
    }
}

bool ConnectDialog::closeOtherTabs()
{
    QMessageBox question(this);
    question.setIcon(QMessageBox::Question);
    question.setText("Закрыть открытые диалоги?");
    question.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
    question.setDefaultButton(QMessageBox::Yes);
    int ret = question.exec();

    if(ret == QMessageBox::Yes)
    {
        // Закрыть все открытые диалоги
        for (int i = 0; i < parentTabWidget->count(); i++)
        {
            if(i != parentTabWidget->indexOf(this))
            {
                qobject_cast<ConversationWidget*>(parentTabWidget->widget(i))->closeProperly();
            }
        }
        return true;
    }
    return false;
}

void ConnectDialog::getComponents()
{
    QPointer<RoQuery> query;
    query = m_component->createSimpleQuery("ROUTER", "query-components");
    query->exec();
}

void ConnectDialog::setActions()
{
    connect(parentTabWidget, SIGNAL(tabCloseRequested(int)),
            this, SLOT(tabCloseButtonPressed(int)));
    connect(connectButton, SIGNAL(clicked()),
            this, SLOT(connectToServer()) );
    connect(disConnectButton, SIGNAL(clicked()),
            this, SLOT(disconnectFromServer()));
    connect(m_module, SIGNAL(connected()),
            this, SLOT(handleConnectedSignal()));
    connect(ComponentsTreeView, SIGNAL(clicked(QModelIndex)),
            this, SLOT(handleTreeViewClick(QModelIndex)));
    connect(TestButton, SIGNAL(clicked()),
            this,SLOT(callFunction()));
    connect(ComponentsTreeView, SIGNAL(doubleClicked(QModelIndex)),
            this, SLOT(handleTreeViewDoubleClick(QModelIndex)) );
    connect(m_component.data(),SIGNAL(receivedComponents(QMap<QString,QString>)),
            this, SLOT(gotComponents(QMap<QString,QString>)));
    connect(m_component.data(), SIGNAL( receivedMethods(QString, QList<Component::Function> ) ),
            this, SLOT( gotMethods( QString, QList<Component::Function>) ) );
    connect(this, SIGNAL(allMethodsReceived()),
            this, SLOT(gotAllMethods()));
    connect(m_component.data(), SIGNAL(receivedNewInfo(RoInfoStanza)),
            this, SLOT(gotNewInfo(RoInfoStanza)));
    connect(sendInfoButton, SIGNAL(clicked()),
            this, SLOT(sendInfo()));
    connect(queryCommandLinkButton, SIGNAL(clicked()),
            this, SLOT (showQueryWidget()));
    connect(infoCommandLinkButton, SIGNAL(clicked()),
            this, SLOT (showInfoWidget()));
    connect(addInfoAttributeButton, SIGNAL(clicked()),
            this, SLOT(addInfoAttribute()));
    connect(addInfoChatButton, SIGNAL(clicked()),
            this, SLOT(addInfoChatWidget()));
}

void ConnectDialog::setInitialData()
{   
    m_module = new RoModule(this, true);
    m_component = new RoDebugViewerComponent(m_module);

    TestButton->setEnabled(false);
    sendInfoButton->setEnabled(false);
    disConnectButton->hide();

    queryWidgetWithButton->hide();
    infoCommandLinkButton->hide();
    infoWidget->hide();
    infoChatInitWidget->hide();

    amountOfComponents = 0;
}

void ConnectDialog::setTextWithCursorInBegin(QLineEdit* const lineEdit, const QString& text)
{
    lineEdit->setText(text);
    lineEdit->setCursorPosition(0);
}
